# coding: utf-8

import json
import time
from math import ceil
from datetime import date, timedelta, datetime

from sqlalchemy import or_, distinct

from common.constant import SpanRule, ColorRule, SizeOperator
from common.demand_util import start_robot_task, demand_to_complete
from common.models import (
    model_factory, ParticStatus, DemandStatus, FollowType,
    UserLevel, EnterpriseLevel,
    DemandCate, DemandColor)
from common.schedule.demand_task import (
    dispatch_demand_handle,
    demand_new_notify_task,
    master_dispatch_handle
)
from common.schedule.demand_bid_task import (
    demand_assign_status_task
)
from common.log import Log


class BaseHandler(object):

    demand_status = object()
    next_mapping = {
        DemandStatus.MASTER_DISPATCHING.value: DemandStatus.DISPATCHING.value,
        DemandStatus.DISPATCHING.value: DemandStatus.BRONZE_MEDAL_DISCOUNT.value,
        DemandStatus.BRONZE_MEDAL_DISCOUNT.value: DemandStatus.ONGOING.value
    }

    def __init__(self, session, demand):
        self.session = session
        self.demand = demand
        self.filter_list = [
            DuplicateFilter(session, demand),
            BidFilter(session, demand),
            DispatchFilter(session, demand)
        ]

    def dispatch_query(self):
        today = date.today()
        user_query = self.session.query(
            model_factory.enterprise_to_user.user_id.label('user_id')
        ).filter(
            model_factory.enterprises.start_date <= today,
            model_factory.enterprises.expired_date >= today
        ).join(
            model_factory.enterprises,
            model_factory.enterprises.id == model_factory.enterprise_to_user.enterprise_id
        )
        return user_query

    def filter(self, users):
        for each_filter in self.filter_list:
            users = each_filter.filter(users)
        return users

    def next_demand_status(self):
        if self.demand_status == self.demand.status:
            self.demand.status = self.next_mapping.get(self.demand_status)

    def decrease_notify_limit(self, users):
        # 首次发布同时还剩可以通知的数量
        if self.demand.published_count == 1 and self.demand.notify_limit > 0:
            # 有可通知的用户
            if users:
                self.demand.notify_limit -= 1

    def publish_handler(self, **kwargs):
        # 项目抢单成功人数超过限制，不进入下一个环节
        had_overflow = demand_to_complete(self.session, self.demand)
        if had_overflow:
            return list()
        return self._publish_handler(**kwargs)

    def _publish_handler(self, **kwargs):
        raise NotImplementedError

    def enterprise_filter(self, enterprise):
        """
        过滤符合规则的企业
        :param enterprise:
        :return:
        """
        if enterprise and enterprise.dispatch_rule:
            try:
                dispatch_rule = json.loads(enterprise.dispatch_rule)

            except Exception as e:
                Log.info(
                    'enterprise_id:{}, error:{}'.format(enterprise.id, repr(e))
                )
                dispatch_rule = list()
            if dispatch_rule:
                for rule in dispatch_rule:
                    try:
                        color_state = self._is_match_color(rule)
                        size_state = self._is_match_size(rule)
                        span_state = self._is_match_span(rule)
                        if color_state and size_state and span_state:
                            return enterprise
                    except Exception as e:
                        Log.info('enterprise_id:{}, rule:{} error:{}'.format(
                            enterprise.id,
                            rule,
                            repr(e))
                        )
                return None
        return enterprise

    def _is_match_span(self, rule):
        """
        create by ld_wu 2018-08-17
        间距是否符合自定义
        :param rule:
        :return:
        """

        span = rule.get('span')
        span_limit = 3
        if span:
            if self.demand.span:
                demand_span = int(self.demand.span.replace('P', ''))
                if span == SpanRule.SMALL.value:
                    return demand_span < span_limit
                elif span == SpanRule.LARGE.value:
                    return demand_span >= span_limit
            return False
        return True

    def _is_match_color(self, rule):
        """
        create by ld_wu 2018-08-17
        颜色是否符合自定义
        :param rule:
        :return:
        """
        color = rule.get('color')
        if color:
            demand_color = self.demand.color
            if color == ColorRule.FULL_COLOR.value:
                return demand_color == DemandColor.FULL_COLOR.value
            elif color == ColorRule.NOT_FULL_COLOR.value:
                return demand_color in (
                    DemandColor.SINGLE_COLOR.value,
                    DemandColor.DOUBLE_COLOR.value
                )
            return False
        return True

    def _is_match_size(self, rule):
        """
        create by ld_wu 2018-08-17
        面积是否符合自定义
        :param rule:
        :return:
        """
        size = rule.get('size')
        size_op = rule.get('size_op')
        if size:
            demand_size = self.demand.size
            size_operator = SizeOperator(size_op)
            return size_operator.op(demand_size, size)
        return True


class MasterDispatchHandler(BaseHandler):
    """
    created by chenhj at 2018/5/24
    主人派单处理(金银牌专属)
    """

    demand_status = DemandStatus.MASTER_DISPATCHING.value

    def is_install(self):
        return self.demand.cate == DemandCate.INSTALL.value

    def _publish_handler(self, **kwargs):
        """
        create by ld_wu 2018-05-15
        :return:
        """
        today = date.today()
        # 已派单的企业
        exclude_enterprise_ids = self.had_dispatch()
        if self.demand.status == self.demand_status and self.is_install():

            # 项目的来源企业
            query = self.session.query(
                model_factory.enterprises
            ).filter(
                model_factory.enterprise_to_area.area_id == self.demand.area_id,
                model_factory.enterprises.start_date <= today,
                model_factory.enterprises.expired_date >= today,
                model_factory.enterprises.leave_dispatch_cnt > 0,
                model_factory.enterprises.level.in_([
                    EnterpriseLevel.LEVEL_4.value,
                    EnterpriseLevel.LEVEL_5.value
                ]),
                model_factory.enterprises.id == self.demand.enterprise_id
            ).join(
                model_factory.enterprise_to_area,
                model_factory.enterprises.id == model_factory.enterprise_to_area.enterprise_id
            )

            if exclude_enterprise_ids:
                query = query.filter(
                    model_factory.enterprises.id.notin_(
                        exclude_enterprise_ids)
                )

            enterprise = query.first()
            # 判断是否符合企业自定义派单规则
            enterprise = self.enterprise_filter(enterprise)
            # 派单模式
            if enterprise:
                enterprise.dispatch_cnt += 1
                enterprise.leave_dispatch_cnt -= 1
                users = self.dispatch_users(enterprise)
                users = self.filter(users)
                if users:
                    now = datetime.utcnow()
                    # 5分钟后进入普通派单模式
                    master_dispatch_handle.apply_async(
                        (self.demand.id, ),
                        eta=now + timedelta(seconds=60 * 5),
                        expires=now + timedelta(seconds=60 * 7)
                    )
                    return users

        self.next_demand_status()
        return DispatchHandler(
            self.session,
            self.demand
        ).publish_handler(
            **kwargs
        )

    def dispatch_users(self, enterprise):
        """
        create by ld_wu 2018-05-15
        派单用户
        :return:
        """
        tag_key = self.demand.tag_key
        user_ids = self.session.query(
            distinct(model_factory.enterprise_to_user.user_id).label('user_id')
        ).filter(
            model_factory.enterprise_to_user.enterprise_id == enterprise.id,
            model_factory.user_to_area.area_id == self.demand.area_id,
            or_(
                getattr(model_factory.user_tags, tag_key).is_(True),
                getattr(model_factory.user_tags, tag_key).is_(None)
            )
        ).join(
            model_factory.user_to_area,
            model_factory.user_to_area.user_id == model_factory.enterprise_to_user.user_id
        ).join(
            model_factory.user_tags,
            model_factory.user_tags.user_id == model_factory.enterprise_to_user.user_id
        ).all()

        # 关注了该地区的企业子账户
        # 当子账号没有关注该地区时，则发给企业主
        user_ids = [each.user_id for each in user_ids] or [enterprise.user_id]

        users = self.session.query(
            model_factory.user
        ).filter(
            model_factory.user.id.in_(user_ids)
        ).all()
        # 派单记录
        users = self.filter(users)
        for user in users:
            model_factory.partic.create(
                self.session,
                demand_id=self.demand.id,
                supplier_id=user.id,
                status=ParticStatus.WAITING.value,
                demand_status=self.demand.status,
                credit=user.credit,
                follow_type=FollowType.NA.value,
                published_count=self.demand.published_count,
                enterprise_id=enterprise.id,
                enterprise_level=enterprise.level
            )
        return users

    def had_dispatch(self):
        """
        create by ld_wu 2018-05-15
        已派单企业id
        :return:
        """
        exclude_enterprises = self.session.query(
            distinct(model_factory.partic.enterprise_id).label('enterprise_id')
        ).filter(
            model_factory.partic.demand_id == self.demand.id,
            model_factory.partic.demand_status.in_([
                DemandStatus.DISPATCHING.value,
                DemandStatus.MASTER_DISPATCHING.value
            ])
        ).all()

        exclude_enterprise_ids = [
            each.enterprise_id for each in exclude_enterprises
        ]
        return exclude_enterprise_ids


class DispatchHandler(MasterDispatchHandler):
    """
    create by ld_wu 2018-05-08
    普通派单处理
    """

    demand_status = DemandStatus.DISPATCHING.value

    def _publish_handler(self, **kwargs):
        """
        create by ld_wu 2018-05-15
        :return:
        """
        today = date.today()
        # 已派单的企业
        exclude_enterprise_ids = self.had_dispatch()
        if self.demand.status == self.demand_status and self.is_install():

            dispatch_enterprises = []

            # 当前可派单的白金企业
            query = self.session.query(
                model_factory.enterprises
            ).filter(
                model_factory.enterprise_to_area.area_id == self.demand.area_id,
                model_factory.enterprises.start_date <= today,
                model_factory.enterprises.expired_date >= today,
                model_factory.enterprises.leave_dispatch_cnt > 0,
                model_factory.enterprises.level == EnterpriseLevel.LEVEL_6.value
            ).join(
                model_factory.enterprise_to_area,
                model_factory.enterprises.id == model_factory.enterprise_to_area.enterprise_id
            )

            if exclude_enterprise_ids:
                query = query.filter(
                    model_factory.enterprises.id.notin_(
                        exclude_enterprise_ids)
                )

            enterprises = query.all()
            for each in enterprises:
                # 判断是否符合企业自定义派单规则
                enterprise = self.enterprise_filter(each)
                if enterprise:
                    dispatch_enterprises.append(enterprise)

            # 当前可派单的金银牌企业
            query = self.session.query(
                model_factory.enterprises
            ).filter(
                model_factory.enterprise_to_area.area_id == self.demand.area_id,
                model_factory.enterprises.start_date <= today,
                model_factory.enterprises.expired_date >= today,
                model_factory.enterprises.leave_dispatch_cnt > 0,
                model_factory.enterprises.level.in_([
                    EnterpriseLevel.LEVEL_4.value,
                    EnterpriseLevel.LEVEL_5.value
                ]),
            ).join(
                model_factory.enterprise_to_area,
                model_factory.enterprises.id == model_factory.enterprise_to_area.enterprise_id
            )

            if exclude_enterprise_ids:
                query = query.filter(
                    model_factory.enterprises.id.notin_(
                        exclude_enterprise_ids)
                )

            other_enterprises = query.order_by(
                model_factory.enterprises.last_dispatch_at.asc(),
                model_factory.enterprises.id.asc()
            ).all()
            other_enterprise = None
            # 找出首个符合企业自定义派单规则的金银牌企业
            for each in other_enterprises:
                if self.enterprise_filter(each):
                    other_enterprise = each
                    break
            if other_enterprise:
                dispatch_enterprises.append(other_enterprise)
                other_enterprise.last_dispatch_at = int(time.time())

            # 派单模式
            if dispatch_enterprises:
                users = []
                for each in dispatch_enterprises:
                    each.dispatch_cnt += 1
                    each.leave_dispatch_cnt -= 1
                    users.extend(self.dispatch_users(each))
                users = self.filter(users)
                if users:
                    now = datetime.utcnow()
                    # 30分钟后判断无人锁定时全面发布的定时任务
                    dispatch_demand_handle.apply_async(
                        (self.demand.id, ),
                        eta=now + timedelta(seconds=60 * 15),
                        expires=now + timedelta(seconds=60 * 17)
                    )
                    return users
        self.next_demand_status()
        return BronzeHandler(
            self.session,
            self.demand
        ).publish_handler(
            exclude_enterprise=exclude_enterprise_ids,
            **kwargs
        )

    @classmethod
    def calc_enterprise_day_disp_cnt(cls, enterprise):
        """
        created by chenhj at 2018/5/24
        计算企业的日均剩余可派单数
        公式：ceil(剩余派单数 / 剩余天数)
        """
        return ceil(
            enterprise.leave_dispatch_cnt /
            ((enterprise.expired_date - date.today()).days or 1)    # 防止分母为0
        )


class BronzeHandler(BaseHandler):
    """
    create by ld_wu 2018-05-08
    铜牌及铜牌有效期
    未派单的用户
    """
    demand_status = DemandStatus.BRONZE_MEDAL_DISCOUNT.value

    def _publish_handler(self, **kwargs):
        """
        create by ld_wu 2018-05-15
        """
        # 铜牌未过期用户
        # 尚未被派单的用户
        exclude_enterprise = kwargs.get('exclude_enterprise', list())
        if self.demand.status == self.demand_status:
            bronze_users = self.valid_bronze_users()
            enterprise_users = self.enterprise_users(
                exclude_enterprise
            )
            bronze_users.extend(enterprise_users)
            bronze_users = self.filter(bronze_users)
            if bronze_users:
                now = datetime.utcnow()
                demand_new_notify_task.apply_async(
                    (self.demand.id,),
                    eta=now + timedelta(seconds=60 * 5),
                    expires=now + timedelta(seconds=60 * 8)
                )
                return bronze_users
        self.next_demand_status()
        return OnGoingHandler(
            self.session,
            self.demand

        ).publish_handler(**kwargs)

    def valid_bronze_users(self):
        """
        create by ld_wu 2018-05-16
        铜牌有效用户
        :return:
        """
        tag_key = self.demand.tag_key
        users = self.session.query(
            model_factory.user
        ).filter(
            model_factory.user.level == UserLevel.LEVEL_3.value,  # 铜牌
            model_factory.user.black.is_(False),
            model_factory.user.active.is_(True),
            model_factory.user.deleted_at == 0,
            or_(
                getattr(model_factory.user_tags, tag_key).is_(True),
                getattr(model_factory.user_tags, tag_key).is_(None)
            ),
            model_factory.user_to_area.area_id == self.demand.area_id,
        ).join(
            model_factory.user_to_area,
            model_factory.user.id == model_factory.user_to_area.user_id
        ).outerjoin(
            model_factory.pack,
            model_factory.user.id == model_factory.pack.uid
        ).outerjoin(
            model_factory.user_tags,
            model_factory.user.id == model_factory.user_tags.user_id
        ).all()
        return users

    def enterprise_users(self, exclude_enterprise):
        """
        create by ld_wu 2018-05-16
        未发送企业子账号
        :return:
        """
        tag_key = self.demand.tag_key
        user_query = self.dispatch_query()
        if exclude_enterprise:
            user_query = user_query.filter(
                model_factory.enterprise_to_user.enterprise_id.notin_(
                    exclude_enterprise
                )
            )
        user_ids = [each.user_id for each in user_query.all()]
        users = list()
        if user_ids:
            users = self.session.query(
                model_factory.user
            ).filter(
                model_factory.user.id.in_(user_ids),
                model_factory.user.black.is_(False),
                model_factory.user.active.is_(True),
                model_factory.user.deleted_at == 0,
                or_(
                    getattr(model_factory.user_tags, tag_key).is_(True),
                    getattr(model_factory.user_tags, tag_key).is_(None)
                ),
                model_factory.user_to_area.area_id == self.demand.area_id,
            ).join(
                model_factory.user_to_area,
                model_factory.user.id == model_factory.user_to_area.user_id
            ).outerjoin(
                model_factory.user_tags,
                model_factory.user.id == model_factory.user_tags.user_id
            ).all()
        return users


class OnGoingHandler(BaseHandler):
    """
    create by ld_wu 2018-05-08

    """
    demand_status = DemandStatus.ONGOING.value

    def _publish_handler(self, **kwargs):
        """
        create by ld_wu 2018-05-16
        直接发竞价：铁牌用户及铜牌用户
        非直接发竞价: 铁牌用户及铜牌已过期（需要过滤企业用户）
        :return:
        """
        direct_notify = kwargs.get('direct_notify', False)
        users = self.notify_users(direct_notify)
        users = self.filter(users)
        return users

    def notify_users(self, direct_notify=False):
        tag_key = self.demand.tag_key

        query = self.session.query(
            model_factory.user
        ).filter(
            model_factory.user.black.is_(False),
            model_factory.user.active.is_(True),
            model_factory.user.deleted_at == 0,
            model_factory.user.type == 100,
            or_(
                getattr(model_factory.user_tags, tag_key).is_(True),
                getattr(model_factory.user_tags, tag_key).is_(None)
            ),
            model_factory.user_to_area.area_id == self.demand.area_id,
        ).join(
            model_factory.user_to_area,
            model_factory.user.id == model_factory.user_to_area.user_id
        ).outerjoin(
            model_factory.user_tags,
            model_factory.user.id == model_factory.user_tags.user_id
        ).outerjoin(
            model_factory.pack,
            model_factory.user.id == model_factory.pack.uid
        )
        if not direct_notify:
            dispatch_users = self.dispatch_query().all()
            query = query.filter(
                model_factory.user.level == UserLevel.LEVEL_2.value
            )
            exclude_user_ids = [each.user_id for each in dispatch_users]
            # 过滤企业子账号
            if exclude_user_ids:
                query = query.filter(
                    model_factory.user.id.notin_(exclude_user_ids)
                )

        # 竞价模式启动5分钟分配任务
        if self.demand.status == self.demand_status:
            now = datetime.utcnow()
            demand_assign_status_task.apply_async(
                (self.demand.id, self.demand.published_count),
                eta=now + timedelta(seconds=60 * 5),
                expires=now + timedelta(seconds=60 * 8)
            )
            start_robot_task(self.demand, self.session)
        return query.all()


class Filter(object):
    """
    create by ld_wu 2018-05-24

    """

    def __init__(self, session, demand):

        self.exclude = list()
        self.session = session
        self.demand = demand
        self.build_exclude()

    def build_exclude(self):
        """
        create by ld_wu 2018-05-24
        构建需要过滤的用户id列表，需子类重写
        :return:
        """
        raise NotImplementedError

    def filter(self, users):
        users = [user for user in users if user.id not in self.exclude]
        return users


class BidFilter(Filter):
    """
    create by ld_wu 2018-05-08
    已抢单或者已经中标及超时未联系
    """

    def build_exclude(self):
        """
        create by ld_wu 2018-05-08
        :return:
        """
        # 本轮已抢过单的及竞拍成功或者超时
        winners = self.session.query(
            model_factory.partic.supplier_id
        ).filter(
            model_factory.partic.deleted_at == 0,
            model_factory.partic.demand_id == self.demand.id,
            model_factory.partic.status.in_([
                ParticStatus.TIMEOUT_NOT_CONTACT.value,
                ParticStatus.BALANCE_NOT_ENOUGH.value,
                ParticStatus.SUCCEED.value
            ])
        ).all()

        self.exclude = {each.supplier_id for each in winners}


class DispatchFilter(Filter):
    """
    过滤已派单用户
    """

    def build_exclude(self):
        """
        已派单的企业子账号
        :return:
        """
        if self.demand.status not in (
            DemandStatus.DISPATCHING.value, DemandStatus.MASTER_DISPATCHING.value
        ):

            dispatch_enterprises = self.session.query(
                distinct(
                    model_factory.partic.enterprise_id
                ).label('enterprise_id')
            ).filter(
                model_factory.partic.demand_id == self.demand.id,
                model_factory.partic.demand_status.in_([
                    DemandStatus.DISPATCHING.value,
                    DemandStatus.MASTER_DISPATCHING.value
                ])
            ).all()

            enterprise_ids = [
                each.enterprise_id for each in dispatch_enterprises
            ]
            if enterprise_ids:
                dispatch_users = self.session.query(
                    model_factory.enterprise_to_user.user_id
                ).filter(
                    model_factory.enterprise_to_user.enterprise_id.in_(
                        enterprise_ids
                    )
                ).all()
                self.exclude = [each.user_id for each in dispatch_users]


class DuplicateFilter(Filter):
    """
    create by ld_wu 2018-05-17
    用户去重
    """
    def build_exclude(self):
        pass

    def filter(self, users):
        mapping = {user.id: user for user in users}
        return list(mapping.values())

